<?php
/*****************************************************************************\
+-----------------------------------------------------------------------------+
| X-Cart                                                                      |
| Copyright (c) 2001-2007 Ruslan R. Fazliev <rrf@rrf.ru>                      |
| All rights reserved.                                                        |
+-----------------------------------------------------------------------------+
| PLEASE READ  THE FULL TEXT OF SOFTWARE LICENSE AGREEMENT IN THE "COPYRIGHT" |
| FILE PROVIDED WITH THIS DISTRIBUTION. THE AGREEMENT TEXT IS ALSO AVAILABLE  |
| AT THE FOLLOWING URL: http://www.x-cart.com/license.php                     |
|                                                                             |
| THIS  AGREEMENT  EXPRESSES  THE  TERMS  AND CONDITIONS ON WHICH YOU MAY USE |
| THIS SOFTWARE   PROGRAM   AND  ASSOCIATED  DOCUMENTATION   THAT  RUSLAN  R. |
| FAZLIEV (hereinafter  referred to as "THE AUTHOR") IS FURNISHING  OR MAKING |
| AVAILABLE TO YOU WITH  THIS  AGREEMENT  (COLLECTIVELY,  THE  "SOFTWARE").   |
| PLEASE   REVIEW   THE  TERMS  AND   CONDITIONS  OF  THIS  LICENSE AGREEMENT |
| CAREFULLY   BEFORE   INSTALLING   OR  USING  THE  SOFTWARE.  BY INSTALLING, |
| COPYING   OR   OTHERWISE   USING   THE   SOFTWARE,  YOU  AND  YOUR  COMPANY |
| (COLLECTIVELY,  "YOU")  ARE  ACCEPTING  AND AGREEING  TO  THE TERMS OF THIS |
| LICENSE   AGREEMENT.   IF  YOU    ARE  NOT  WILLING   TO  BE  BOUND BY THIS |
| AGREEMENT, DO  NOT INSTALL OR USE THE SOFTWARE.  VARIOUS   COPYRIGHTS   AND |
| OTHER   INTELLECTUAL   PROPERTY   RIGHTS    PROTECT   THE   SOFTWARE.  THIS |
| AGREEMENT IS A LICENSE AGREEMENT THAT GIVES  YOU  LIMITED  RIGHTS   TO  USE |
| THE  SOFTWARE   AND  NOT  AN  AGREEMENT  FOR SALE OR FOR  TRANSFER OF TITLE.|
| THE AUTHOR RETAINS ALL RIGHTS NOT EXPRESSLY GRANTED BY THIS AGREEMENT.      |
|                                                                             |
| The Initial Developer of the Original Code is Ruslan R. Fazliev             |
| Portions created by Ruslan R. Fazliev are Copyright (C) 2001-2007           |
| Ruslan R. Fazliev. All Rights Reserved.                                     |
+-----------------------------------------------------------------------------+
\*****************************************************************************/

#
# $Id: sessions.php,v 1.62.2.24 2007/09/12 07:04:47 max Exp $
#

if ( !defined('XCART_START') ) { header("Location: ../"); die("Access denied"); }

#
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# DO NOT CHANGE ANYTHING BELOW THIS LINE UNLESS
# YOU REALLY KNOW WHAT ARE YOU DOING
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#

if (defined('XCART_SESSION_START'))
	return;

define("XCART_SESSION_START", 1);

if ($use_sessions_type == 2)
	require_once $xcart_dir."/include/mysql_sessions.php";

#
# PHP build-in sessions tuning (for type "1" & "2")
#

# PHP 4.3.0 and higher allow to turn off trans-sid using this command:
ini_set("url_rewriter.tags","");
# Let's garbage collection will occurs more frequently
ini_set("session.gc_probability",90);
ini_set("session.gc_divisor",100); # for PHP >= 4.3.0
ini_set("session.use_cookies", false);

#
# Anti cache block
#

if (defined("SET_EXPIRE")) {
	header("Expires: ".gmdate("D, d M Y H:i:s", SET_EXPIRE)." GMT");
} else {
	header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
}
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");

if (defined("SET_EXPIRE")) {
	header("Cache-Control: public");
}
elseif ($HTTPS) {
	header("Cache-Control: private, must-revalidate");
}
else {
	header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
	header("Pragma: no-cache");
}

#
# P3P header
#
if (!empty($config['Security']['p3p_compact_policy']))
	header("P3P: " . (!empty($config['Security']['p3p_policyref']) ? "policyref=\"{$config['Security']['p3p_policyref']}\", " : "") . "CP=\"{$config['Security']['p3p_compact_policy']}\"");

#
# Get session id
#
if (isset($HTTP_POST_VARS[$XCART_SESSION_NAME])) {
	$session_source = 'P';
	$XCARTSESSID = $HTTP_POST_VARS[$XCART_SESSION_NAME];

} elseif (isset($HTTP_GET_VARS[$XCART_SESSION_NAME])) {
	$session_source = 'G';
	$XCARTSESSID = $HTTP_GET_VARS[$XCART_SESSION_NAME];

} elseif (isset($HTTP_COOKIE_VARS[$XCART_SESSION_NAME])) {
	$session_source = 'C';
	$XCARTSESSID = $HTTP_COOKIE_VARS[$XCART_SESSION_NAME];

} else {
	$session_source = false;
	$XCARTSESSID = false;
}

if (defined("USE_SESSION_HISTORY") && constant("USE_SESSION_HISTORY")) {
	$session_host = $HTTP_HOST;
	$is_session_exists = !empty($XCARTSESSID) && func_query_first_cell("SELECT COUNT(*) FROM $sql_tbl[sessions_data] WHERE sessid = '$XCARTSESSID'") > 0;

	# Remembers xid
	if (!empty($XCARTSESSID) && !$is_session_exists) {

		if ($session_source == 'C') {

			# Remembers xid if user goto old host
			$remember_xid = func_query_first_cell("SELECT dest_xid FROM $sql_tbl[session_history] WHERE ip = '$REMOTE_ADDR' AND host = '$session_host' AND xid = '$XCARTSESSID'");
			if ($remember_xid) {
				$XCARTSESSID = $remember_xid;
				$session_source = 'R';
			}

		} elseif (in_array($session_source, array('G', 'P')) && isset($HTTP_COOKIE_VARS[$XCART_SESSION_NAME]) && func_query_first_cell("SELECT COUNT(*) FROM $sql_tbl[sessions_data] WHERE sessid = '".$HTTP_COOKIE_VARS[$XCART_SESSION_NAME]."'")) {

			# Remembers xid if user goto cached page with old xid if form / link
			$XCARTSESSID = $HTTP_COOKIE_VARS[$XCART_SESSION_NAME];
			$session_source = 'R';
		}
	}
}

$init_xid = $XCARTSESSID;
x_session_start($XCARTSESSID);

register_shutdown_function("x_session_save");
func_setcookie($XCART_SESSION_NAME, $XCARTSESSID, 0, "/", $xcart_https_host, 0);
func_setcookie($XCART_SESSION_NAME, $XCARTSESSID, 0, "/", $xcart_http_host, 0);

# Save current xid for current host
if (defined("USE_SESSION_HISTORY") && constant("USE_SESSION_HISTORY") && !empty($HTTP_HOST) && func_is_valid_ip($REMOTE_ADDR)) {
	$old_xid = func_query_first_cell("SELECT xid FROM $sql_tbl[session_history] WHERE ip = '$REMOTE_ADDR' AND host = '$session_host'");
	if (empty($old_xid)) {
		func_array2insert(
			"session_history",
			array(
				"ip" => $REMOTE_ADDR,
				"host" => $session_host,
				"xid" => $XCARTSESSID
			),
			true
		);

	} else {
		func_array2update("session_history", array("xid" => $XCARTSESSID, 'dest_xid' => ''), "ip = '$REMOTE_ADDR' AND host = '$session_host'");
	}

	# Update destination xid for other hosts
	if (!empty($init_xid) && $init_xid != $XCARTSESSID) {
		db_query("UPDATE $sql_tbl[session_history] SET dest_xid = '$XCARTSESSID' WHERE ip = '$REMOTE_ADDR' AND host != '$session_host' AND (xid = '$init_xid' OR dest_xid = '$init_xid')");
	}
}

$smarty->assign("XCARTSESSNAME", $XCART_SESSION_NAME);
$smarty->assign("XCARTSESSID", $XCARTSESSID);

#
# Remove xid from URL
#
if ($REQUEST_METHOD == 'GET' && !empty($QUERY_STRING) && preg_match("/(?:^|\&)".preg_quote($XCART_SESSION_NAME, "/")."=[\d\w]+/", $QUERY_STRING)) {
	$qs = func_qs_remove($QUERY_STRING, $XCART_SESSION_NAME);
	func_header_location($PHP_SELF.(empty($qs) ? "" : ("?".$qs)));
}

####################################################################
#   FUNCTIONS
####################################################################

#
# Start session
#
function x_session_start($sessid = '') {
	global $XCART_SESSION_VARS, $XCART_SESSION_NAME, $XCARTSESSID, $XCART_SESSION_EXPIRY;
	global $sql_tbl, $config, $use_sessions_type;
	global $HTTP_SERVER_VARS;

	# $sessid should contain only '0'..'9' or 'a'..'z' or 'A'..'Z'
	if (strlen($sessid) > 32 || !empty($sessid) && !preg_match('!^[0-9a-zA-Z]+$!S', $sessid)) {
		$sessid = "";
	}

	$XCART_SESSION_VARS = array();
	#
	# For new sessions always generate unique id
	#

	$l = 0;
	if (isset($HTTP_SERVER_VARS["REMOTE_PORT"]))
		$l = $HTTP_SERVER_VARS["REMOTE_PORT"];

	list($usec, $sec) = explode(' ', microtime());
	srand((float) $sec + ((float) $usec * 1000000) + (float)$l);

	$sessid_is_empty = empty($sessid);
	if (!$sessid_is_empty && ($use_sessions_type == 2 || $use_sessions_type == 3)) {
		if (func_query_first_cell("SELECT COUNT(*) FROM $sql_tbl[sessions_data] WHERE sessid='$sessid'") == 0)
			$sessid = "";
	}

	if (empty($sessid)) {
		$sessid = x_session_generateid();
	}

	if (empty($config["Sessions"]["session_length"]))
		$config["Sessions"]["session_length"] = 30;

	if ($use_sessions_type == 1 && intval(ini_get("session.gc_maxlifetime")) != $config["Sessions"]["session_length"] && function_exists("ini_set"))
		@ini_set("session.gc_maxlifetime", $config["Sessions"]["session_length"]);

	if ($use_sessions_type < 3) {
		if ($use_sessions_type == 2) {
			# restore handler for mysql sessions
			session_set_save_handler (
				'db_session_open',
				'db_session_close',
				'db_session_read',
				'db_session_write',
				'db_session_destroy',
				'db_session_gc'
				);
		}

		#
		# Using standard PHP sessions
		#
		session_cache_limiter('none');
	
		session_name($XCART_SESSION_NAME);
		if ($sessid)
			session_id($sessid);

		session_start();

		if (strlen(session_encode()) == 0) {
			define("NEW_SESSION", true);
			if (!$sessid_is_empty && $use_sessions_type == 1) {
				$sessid = x_session_generateid();
				session_id($sessid);
			}
		}

		global $_saved_session_fg, $_session_force_regenerate;
		x_session_register("_saved_session_fg");
		x_session_register("_session_force_regenerate");
		if (!defined("NEW_SESSION") && !func_skip_fingerprint_check() && $_saved_session_fg != x_session_get_fg()) {

			# Check session by fingerprint
			session_destroy();
			$sessid = x_session_generateid();
			session_id($sessid);
			session_start();

        } elseif ($_session_force_regenerate || func_check_regen_sess_condition()) {

            # Change session
			$old_sessid = $sessid;
			$sessid = x_session_generateid();
			func_session_change($old_sessid, $sessid);
			session_id($sessid);
			if ($use_sessions_type == 2) {
				db_query("UPDATE $sql_tbl[sessions_data] SET sessid = '$sessid' WHERE sessid = '$old_sessid'");
			}
        }

		$XCARTSESSID = session_id();

		x_session_unregister("_saved_session_fg");
		x_session_unregister("_session_force_regenerate");
		x_session_register("_session_force_regenerate");
        x_session_register("_saved_session_fg", x_session_get_fg());
        $_saved_session_fg = x_session_get_fg();

		func_setcookie($XCART_SESSION_NAME, $XCARTSESSID, 0, "/", "", 0);

		return;
	}
	
	$curtime = time();
	$expiry_time = $curtime + $config["Sessions"]["session_length"];

	# Delete expired sessions
	$delete_ids = func_query_column("SELECT sessid FROM $sql_tbl[sessions_data] WHERE expiry < '".time()."'");
	if (!empty($delete_ids)) {
		db_query("DELETE FROM $sql_tbl[sessions_data] WHERE expiry < '".time()."'");

		func_delete_session_related_data($delete_ids);
	}
	
	$sess_data = func_query_first("SELECT * FROM $sql_tbl[sessions_data] WHERE sessid='$sessid'");

	if ($sess_data) {
		$XCART_SESSION_VARS = unserialize($sess_data["data"]);

		if (!func_skip_fingerprint_check() && $XCART_SESSION_VARS['_saved_session_fg'] != x_session_get_fg()) {

			# Check session by fingerprint
			$sess_data = false;
			db_query("DELETE FROM $sql_tbl[sessions_data] WHERE sessid='$sessid'");
			$sessid = x_session_generateid();

		} elseif ($XCART_SESSION_VARS['_session_force_regenerate'] || func_check_regen_sess_condition()) {

			# Change session
			$new_sessid = x_session_generateid();
			func_session_change($sessid, $new_sessid);
			db_query("UPDATE $sql_tbl[sessions_data] SET sessid='$new_sessid' WHERE sessid='$sessid'");
			$sessid = $new_sessid;
		}
	}

	if (empty($sess_data)) {
		if (!defined("NEW_SESSION"))
			define("NEW_SESSION", true);

		$XCART_SESSION_VARS['_saved_session_fg'] = x_session_get_fg();
		$XCART_SESSION_VARS = array(
			"_saved_session_fg" => $XCART_SESSION_VARS['_saved_session_fg']
		);
		
		db_query("REPLACE INTO $sql_tbl[sessions_data] (sessid, start, expiry, data) VALUES('$sessid', '$curtime', '$expiry_time', '".addslashes(serialize($sess_data))."')");

	} elseif ($XCART_SESSION_VARS['_saved_session_fg'] != x_session_get_fg()) {

		$XCART_SESSION_VARS['_saved_session_fg'] = x_session_get_fg();
		db_query("UPDATE $sql_tbl[sessions_data] SET data = '".addslashes(serialize($XCART_SESSION_VARS))."', expiry='$expiry_time' WHERE sessid = '$sessid'");

	} else {
		db_query("UPDATE $sql_tbl[sessions_data] SET expiry='$expiry_time' WHERE sessid='$sessid'");
	}

	$XCART_SESSION_EXPIRY = $expiry_time;
	$XCARTSESSID = $sessid;
	func_setcookie($XCART_SESSION_NAME, $XCARTSESSID, 0, "/", "", 0);
}

#
# Change current session to session with specified ID
#
function x_session_id($sessid="") {
	global $sql_tbl, $use_sessions_type, $XCART_SESSION_VARS, $XCARTSESSID, $XCART_SESSION_UNPACKED_VARS;

	$XCART_SESSION_VARS = array();
	if ($use_sessions_type < 3) {
		#
		# Using standard PHP sessions
		#
		if ($sessid) {
			session_write_close();
			x_session_start($sessid);
			return;
		}

		$XCARTSESSID = session_id();

		return $XCARTSESSID;
	}

	if ($sessid) {
		$sess_data = func_query_first("SELECT * FROM $sql_tbl[sessions_data] WHERE sessid='$sessid'");
		$XCARTSESSID = $sessid;
		if ($sess_data) {
			$XCART_SESSION_VARS = unserialize($sess_data["data"]);
			if (!empty($XCART_SESSION_UNPACKED_VARS)) {
				foreach ($XCART_SESSION_UNPACKED_VARS as $var => $v) {
					if (isset($GLOBALS[$var]))
						unset($GLOBALS[$var]);

					unset($XCART_SESSION_UNPACKED_VARS[$var]);
				}
			}
		}
		else {
			x_session_start($sessid);
		}
	}
	else {
		$sessid = $XCARTSESSID;
	}

	return $sessid;
}

#
# Cut off variable if it is come from _GET, _POST or _COOKIES
#
function check_session_var($varname) {
	global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;

	if (isset($HTTP_GET_VARS[$varname]) || isset($HTTP_POST_VARS[$varname]) || isset($HTTP_COOKIE_VARS[$varname]))
		return false;

	return true;
}

#
# Register variable XCART_SESSION_VARS array from the database
#
function x_session_register($varname, $default="") {
	global $XCART_SESSION_VARS, $XCART_SESSION_UNPACKED_VARS;
	global $use_sessions_type;
	global $HTTP_SESSION_VARS;
	
	if (empty($varname))
		return false;

	if ($use_sessions_type < 3) {
		#
		# Using standard PHP sessions
		#
		if (!session_is_registered($varname) && check_session_var($varname)) {
			$HTTP_SESSION_VARS[$varname] = $default;
		}

		session_register($varname);

		#
		# Register global variable
		#
		$GLOBALS[$varname] =& $HTTP_SESSION_VARS[$varname];
		return;
	}

	#
	# Register variable $varname in $XCART_SESSION_VARS array
	#
	if (!isset($XCART_SESSION_VARS[$varname])) {
		if (isset($GLOBALS[$varname]) && check_session_var($varname)) {
			$XCART_SESSION_VARS[$varname] = $GLOBALS[$varname];
		}
		else {
			$XCART_SESSION_VARS[$varname] = $default;
		}
	}
	else {
		if (isset($GLOBALS[$varname]) && check_session_var($varname)) {
			$XCART_SESSION_VARS[$varname] = $GLOBALS[$varname];
		}
	}

	#
	# Unpack variable $varname from $XCART_SESSION_VARS array
	#
	$XCART_SESSION_UNPACKED_VARS[$varname] = $XCART_SESSION_VARS[$varname];
	$GLOBALS[$varname] = $XCART_SESSION_VARS[$varname];
}

#
# Save the XCART_SESSION_VARS array in the database
#
function x_session_save() {
	global $XCARTSESSID;
	global $XCART_SESSION_VARS, $XCART_SESSION_UNPACKED_VARS;
	global $sql_tbl, $use_sessions_type, $bench_max_session;

	if ($use_sessions_type < 3) {
		#
		# Using standard PHP sessions
		#
		return;
	}

	$varnames = func_get_args();
	if (!empty($varnames)) {
		foreach ($varnames as $varname) {
			if (isset($GLOBALS[$varname]))
				$XCART_SESSION_VARS[$varname] = $GLOBALS[$varname];
		}
	}
	elseif (is_array($XCART_SESSION_UNPACKED_VARS)) {
		foreach ($XCART_SESSION_UNPACKED_VARS as $varname=>$value) {
			if (isset($GLOBALS[$varname]))
				$XCART_SESSION_VARS[$varname] = $GLOBALS[$varname];
		}
	}

	#
	# Save session variables in the database
	#
	if (defined("BENCH") && constant("BENCH")) {
		$len = strlen(serialize($XCART_SESSION_VARS));
		if ($bench_max_session < $len)
			$bench_max_session = $len;
	}

	db_query("UPDATE $sql_tbl[sessions_data] SET data='".addslashes(serialize($XCART_SESSION_VARS))."' WHERE sessid='$XCARTSESSID'");
}

#
# Unregister variable $varname from $XCART_SESSION_VARS array
#
function x_session_unregister($varname, $unset_global=false) {
	global $XCART_SESSION_VARS, $XCART_SESSION_UNPACKED_VARS;
	global $use_sessions_type;

	if (empty($varname))
		return false;

	if ($use_sessions_type < 3) {
		#
		# Using standard PHP sessions
		#
		session_unregister($varname);
		return;
	}

	func_unset($XCART_SESSION_VARS, $varname);
	func_unset($XCART_SESSION_UNPACKED_VARS, $varname);

	if ($unset_global) {
		func_unset($GLOBALS, $varname);
	}
}

#
# Find out whether a global variable $varname is registered in 
# $XCART_SESSION_VARS array
#
function x_session_is_registered($varname) {
	global $XCART_SESSION_VARS;
	global $use_sessions_type;

	if (empty($varname))
		return false;

	if ($use_sessions_type < 3) {
		#
		# Using standard PHP sessions
		#
		return session_is_registered($varname);
	}

	return isset($XCART_SESSION_VARS[$varname]);
}

#
# Generate unique session id
#
function x_session_generateid() {
	global $use_sessions_type, $sql_tbl;

	do {
		$sessid = md5(uniqid(rand()));
		$already_exists = false;
		if ($use_sessions_type == 2 || $use_sessions_type == 3)
			$already_exists = func_query_first_cell("SELECT COUNT(*) FROM $sql_tbl[sessions_data] WHERE sessid='$sessid'") > 0;

	} while ($already_exists);

	return $sessid;
}

#
# Get session fingerprint
#
function x_session_get_fg() {
	global $HTTP_USER_AGENT;

	return md5($HTTP_USER_AGENT);
}

#
# Check - regenerate session or not
#
function func_check_regen_sess_condition() {
	global $PHP_SELF, $REQUEST_METHOD;

	return preg_match("/login\.php/", $PHP_SELF) && $REQUEST_METHOD == 'POST';
}

#
# Check - skip or not session fingerprint checking procedure
#
function func_skip_fingerprint_check() {
	return defined("SKIP_CHECK_SESSGION_FG");
}

#
# Delete session related data
#
function func_delete_session_related_data($delete_ids) {
	global $sql_tbl;

	if (empty($delete_ids) || !is_array($delete_ids))
		return false;

	if (defined("USE_SESSION_HISTORY") && constant("USE_SESSION_HISTORY"))
		db_query("DELETE FROM $sql_tbl[session_history] WHERE dest_xid IN ('".implode("','", $delete_ids)."') OR xid IN ('".implode("','", $delete_ids)."')");

	if (defined("AREA_TYPE") && (constant("AREA_TYPE") == 'A' || constant("AREA_TYPE") == 'P')) {

		# Erase old service array (Group editing of products functionality)
		$res = db_query("SELECT geid FROM $sql_tbl[ge_products] WHERE sessid IN ('".implode("','", $delete_ids)."')");
		if ($res) {
			while ($row = db_fetch_row($res)) {
				func_ge_erase($row[0]);
			}
			db_free_result($res);
		}

		# Erase form ids
		db_query("DELETE FROM $sql_tbl[form_ids] WHERE sessid IN ('".implode("','", $delete_ids)."')");

	} elseif (defined("AREA_TYPE") && constant("AREA_TYPE") == 'C') {

		# Erase old service array (3D secure service data)
		db_query("DELETE FROM $sql_tbl[secure3d_data] WHERE sessid IN ('".implode("','", $delete_ids)."')");
	}

	return true;
}

#
# Change session related data if session id s changed
#
function func_session_change($old_sessid, $new_sessid) {
	global $sql_tbl;

	db_query("UPDATE $sql_tbl[form_ids] SET sessid = '$new_sessid' WHERE sessid = '$old_sessid'");
	db_query("UPDATE $sql_tbl[ge_products] SET sessid = '$new_sessid' WHERE sessid = '$old_sessid'");
	db_query("UPDATE $sql_tbl[secure3d_data] SET sessid = '$new_sessid' WHERE sessid = '$old_sessid'");
	db_query("UPDATE $sql_tbl[users_online] SET sessid = '$new_sessid' WHERE sessid = '$old_sessid'");

	return true;
}

?>
